jQuery(function() {
    var $ = jQuery;

    if(typeof vidoop_secure == 'undefined') {
      return;
    }
    
    var debug = vidoop_secure.debug;
    var api_url = vidoop_secure.api_url || 'https://api.vidoop.com/';
    var start_text = vidoop_secure.start_text || 'Click here to complete';
    var start_text_hover = vidoop_secure.start_text_hover || 'An image will appear under this field when you click here. This field is required.';
    var close_text = vidoop_secure.close_text || 'Press Escape or Tab keys to close.';
    var hide_text = vidoop_secure.hide_text || 'close';
    var image_credits_text = vidoop_secure.image_credits_text || 'image credits';
    var step1_text = vidoop_secure.step1_text || '<strong>1</strong> Find the picture of <span class="vs_current_category"></span>';
    var step2_text = vidoop_secure.step2_text || '<strong>2</strong> Type the letter that appears in the picture of <span class="vs_current_category"></span>';
    var not_word_text = vidoop_secure.not_word_text || 'TIP: <strong>Don\'t actually type the word "<span class="vs_current_category"></span></strong>". Type the letter on the picture.';
    var bad_chars_text = vidoop_secure.bad_chars_text || "Your answer should not contain numbers or symbols.";
    var done_typing_text = vidoop_secure.done_typing || 'Great! You may <a href="#" class="vs_close">close this now</a>.';

    function to_hex(n) {
        return ("0" + Math.floor(n).toString(16)).slice(-2);
    }

    function color_fade(color1, color2, percent) {
        return [color1[0] + percent * (color2[0] - color1[0]),
            color1[1] + percent * (color2[1] - color1[1]),
            color1[2] + percent * (color2[2] - color1[2])];
    }

    function to_color_array(str) {
        var match = str.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i);
        for (var i = 1; i < match.length; i++) match[i] = parseInt(match[i], 16);
        return match.slice(1);
    }

    function to_color_hex(color) {
        return '#' + to_hex(color[0]) + to_hex(color[1]) + to_hex(color[2]);
    }

    // Add placeholder text to an input box
    function add_placeholder(input, placeholder) {

        // code that's executed whenever input field is not focused
        function onblur() {
            $.data(this, 'maxlength', $(this).attr('maxlength'));
            if (!this.value || this.value == placeholder) {
                $(this).addClass("vs_placeholder").attr('maxlength', placeholder.length).val(placeholder);
            }
        }

        $(input).each(onblur).blur(onblur).focus(function() {
            // reset style on focus
            if ($(this).hasClass("vs_placeholder")) {
                $(this).removeClass("vs_placeholder").val('').attr('maxlength', $.data(this, 'maxlength'));
            }
        });

        $(input.form).submit(function() {
            $(this).find('input.vs_placeholder').val('');
        });
    }

    $.fn.extend({
        safeattr: function(prop, val) {
            return this.each(function() {
                // IE returns a stupid large integer if maxlength is not set.
                if (prop == "maxlength" && this.getAttribute(prop) > 100000 ||
                    !this.getAttribute(prop)) $(this).attr(prop, val);
            });
        },
        vs_highlight: function(start, end, interval) {
            return this.each(function() {
                if ($.data(this, 'highlight_interval')) {
                    clearInterval($.data(this, 'highlight_interval'));
                }

                var start_array = to_color_array(start);
                var end_array = to_color_array(end);
                var percent;

                var time_end;
                var self = this;

                var handle = setInterval(function() {
                    var now = new Date().getTime();
                    if (!time_end) time_end = now + interval;
                    if (now <= time_end) {
                        var progress = (time_end - now) / interval;
                        $(self).css('background-color', to_color_hex(color_fade(
                            start_array, end_array, (1 - progress) * (1 - progress))));
                    }
                    else {
                        clearInterval(handle);
                        $(self).css('background-color', end);
                        $.data(self, 'highlight_interval', null);
                    }
                }, 50);
                $.data(this, 'highlight_interval', handle);
            });
        }
    });

    // Constructor for a lightbox-style captcha object.
    function Captcha($input, captcha_id) {
        this.small = { width: 180, height: 1 };

        // Anchor the slideout to the node that was passed in
        this.anchor = $("<div style='position: relative; clear: both;'></div>").insertAfter($input);

        // This is the widget that slides out.
        this.$slider = $('<div class="vs_bubble"><p>' + get_instructions() + '</p><div class="vs_instructions_wrapper">' +
            '<div class="vs_arrow_container">' +
            '    <div class="vs_instruction_part">' + step1_text + '</div>' +
            '    <div class="vs_arrow"></div>' +
            '</div>' +
            '<div class="vs_then">then</div>' +
            '<div class="vs_instruction_part">' + step2_text + '</div>' +
            '</div><div class="vs_hide"><a class="vs_close" href="#">' + hide_text + ' &times;</a></div><p class="vs_hint"></p>' +
            '<img src="' + api_url + 'vs/captchas/' + captcha_id + '/image"><div><div class="vs_links"><a target="_blank" alt="powered by vidoop" href="http://vidoop.com/captcha/" class="vs_logo"><span>t</span></a><a target="_blank" href="https://myvidoop.com/attribution">' + image_credits_text + '</a></span></div></div>')
            .appendTo(this.anchor)
            .css({ opacity: 0 });

        var me = this;
        this.input = $input[0];

        var categories = $('.vidoop_secure').find('.category_name');

        var wrong_number_text = vidoop_secure.wrong_number_text || "You should type " + categories.length + " letters in total: one for each picture you've been asked to find.";

        var is_typing_category = categories.map(function() { 
            return new RegExp(this.firstChild.nodeValue.substr(0,5)); });
        var last_typed_category;
        var last_letter;

        $(".vidoop_secure .vs_bubble").mousedown(function(e) {
            if (!$(e.target).hasClass("vs_close")) {
                e.preventDefault();
                setTimeout(function() { $input.trigger("focus"); }, 0);
            }
        });

        $input.bind("slide_out", function() {
            var stripped_length = this.value.replace(/[^A-Za-z]+/g, "").length;
            if ($(this).hasClass("vs_placeholder") ||
                    stripped_length < categories.length) {
                $(".vidoop_secure:not(.vs_error) .vs_hint")
                    .html(wrong_number_text).show();
                $(".vidoop_secure").addClass('vs_error');
            }
        });

        $input.focus(function(e) {
            $(this).trigger("keydown");
            me.slide_in(e);
            me.focused = true;
        }).blur(function(e) {
            me.focused = false;
            me.slide_out(e);
        }).bind("keypress keydown", function(e) {
            if (e.keyCode == 27 || e.keyCode == 13) {
                e.preventDefault();
                this.blur();

                // Find any element after the captcha field to focus
                var found = false;
                for (var i = 0; i < this.form.elements.length; i++) {
                    if (found && $(this.form.elements[i]).attr('type') != 'hidden') {
                        this.form.elements[i].focus();
                        break;
                    }
                    if (this == this.form.elements[i]) {
                        found = true;
                    }
                }
            }
            else {
                var me = this;
                setTimeout(function() {
                    // Which letter are they now looking for?
                    var stripped_length = me.value.replace(/[^A-Za-z]+/g, "").length;
                    var current_letter = Math.min(stripped_length, categories.length);

                    for (var i = 0; i < categories.length; i++) {
                        if (is_typing_category[i].test(me.value) &&
                                last_typed_category != i) {
                            last_typed_category = i;
                            $(".vidoop_secure .vs_hint").html(not_word_text)
                                .show().find('.vs_current_category')
                                .html($.trim(categories.eq(i).html()));
                            $(".vidoop_secure").addClass('vs_error');
                        }
                    }

                    if (current_letter != last_letter) {
                        last_letter = current_letter;

                        // Highlight that category
                        categories.removeClass("vs_current")
                            .eq(current_letter).addClass("vs_current");
                        // Mark earlier categories as done.
                        categories.removeClass("vs_done").slice(0, current_letter)
                            .addClass("vs_done");
                        if (current_letter < categories.length) {
                            // Update current category text in instructions.
                            $(".vs_current_category").html(categories[current_letter]
                                .firstChild.nodeValue)
                                .eq(0).vs_highlight('#ffff55', '#ffffff', 1000);
                            $(".vs_hint").hide();
                        }
                        $(".vidoop_secure").removeClass('vs_error');
                    }

                    if (/[^A-Za-z, ]/.test(me.value)) {
                        $(".vidoop_secure .vs_hint").html(bad_chars_text)
                            .show().addClass('vs_error_badchars');
                        $(".vidoop_secure").addClass('vs_error');
                    }
                    else {
                        var error = $(".vidoop_secure .vs_hint.vs_error_badchars")
                            .removeClass("vs_error_badchars").hide();
                        if (error.length) $(".vidoop_secure").removeClass('vs_error');
                    }

                    if (!/[^a-z]/i.test(me.value) &&
                            stripped_length == categories.length) {
                        // Tell them they can close the window.
                        last_typed_category = null;
                        $(".vidoop_secure .vs_hint").html(done_typing_text)
                            .show().vs_highlight('#ffff55', '#eeeeee', 1000);
                        $(".vidoop_secure").removeClass('vs_error');
                    }
                    else if (stripped_length > categories.length) {
                        // Too many letters
                        $(".vidoop_secure:not(.vs_error) .vs_hint")
                            .html(wrong_number_text).show();
                        $(".vidoop_secure").addClass('vs_error');
                    }

                }, 0);
            }
        })
        .safeattr('title', start_text_hover)
        .safeattr('autocomplete', 'off')
        .safeattr('maxlength', '100')

        $(".vidoop_secure .vs_bubble img").load(function() {
            add_placeholder($input[0], start_text);

            me.$slider.css('display', 'block');
            me.large = { width: me.$slider.width() };
            me.$slider.css('width', me.large.width);
            me.large.height = me.$slider.height();
            me.$slider.css({
                'display': 'none',
                'width': me.small.width,
                'height': me.small.height
            });

            // Ensures that the page is long enough when captcha slides out.
            // Otherwise tabbing to the next field slides the page up.
            $('<div style="visibility:hidden;position:absolute;height:1px;">x</div>')
                .appendTo(me.anchor)
                .css('top', me.large.height);


            if (me.focused) {
                $(me.input).trigger("focus");
            }
            else {
                $(me.input).trigger("blur");
            }
        });
    }

    Captcha.prototype = {
        // show the captcha
        slide_in: function(e) {
            var me = this;

            if (this.large && !me.shown) {
                me.shown = true;
                this.$slider.stop().animate({
                    'width': this.large.width,
                    'height': this.large.height,
                    'opacity': 1
                }, function() {
                    var scrollTop = window.scrollY || document.body.scrollTop || document.documentElement.scrollTop;
                    window.scrollTo(0, scrollTop + me.$slider[0].offsetTop + me.large.height);
                    me.$slider.css('height', 'auto');
                });

                $(me.input).trigger("slide_in");
            }
        },

        // hide the captcha
        slide_out: function(e) {
            var me = this;

            if (me.shown) {
                me.shown = false;
                this.$slider.stop().animate({
                    width: this.small.width,
                    height: this.small.height,
                    opacity: '0'
                }, function() {
                    me.$slider.hide();
                    $(me.input).trigger("slide_out");
                });
            }
        },

        // toggle whether captcha is shown or hidden
        slide_toggle: function(e) {
            if (this.$slider.css('display') == "none") {
                this.slide_in(e);
            }
            else {
                this.slide_out(e);
            }
        }
    };

    // adds CSS styling to the page
    function add_style() {
        $(['<style type="text/css">',
           '.vidoop_secure { position: relative; }',
           '.vidoop_secure.vs_error input { background: url(http://com.vidoop.demo-static.s3.amazonaws.com/images/error.gif) right center no-repeat; }',
           '.vidoop_secure .vs_hint { display: none; margin: 0px; padding: 0px; }',
           '.vidoop_secure.vs_error .vs_hint { background: url(http://com.vidoop.demo-static.s3.amazonaws.com/images/error.gif) left center no-repeat; padding-left: 18px; }',
           '.vidoop_secure .vs_hide { position: absolute; right: 10px; top: 10px; font-size: 12px; }',
           '.vidoop_secure .vs_bubble { padding: 3px 15px; position: absolute; z-index: 999; display: none; background-color: #eee; border: solid 3px #444; margin-bottom: 4px; -moz-border-radius: 5px; -webkit-border-radius: 5px; }',
           '.vidoop_secure p { text-align: left; margin: 10px 0px; display: block; width: 90%; font-size: 12px; color: #444; }',
           '.vidoop_secure .in_order{font-weight:bold; }',
           '.vidoop_secure .category_name{ font-size: 12px; }',
           '.vidoop_secure .vs_placeholder { color: #00e; text-decoration: underline; }',
           '.vidoop_secure .vs_links { text-align: right; font-size: 9px; clear: both; }',
           '.vidoop_secure .vs_links .vs_logo { margin: auto; padding-left: 78px; background: url(http://com.vidoop.demo-static.s3.amazonaws.com/images/vidoop_captcha_logo_medium.gif) left center no-repeat; }',
           '.vidoop_secure .vs_links .vs_logo span { background-color: black; visibility: hidden; }',
           '.vidoop_secure .vs_instructions_wrapper { text-align: left; position: relative;}',
           '.vs_instruction_part { ',
              'border: 1px solid #bebebe;',
              'background-color: white;',
              'left: 42%;',
              'line-height: 1;',
              'padding: 9px;',
              'width: 55%;',
              '-moz-border-radius: 1.5em;',
              '-webkit-border-radius: 1em;',
           '}',
           '.vs_instruction_part, .vs_then, .vs_arrow_container { ',
               'text-align: center;',
               'top: 0;',
               'font-size: 12px;',
               'position: absolute;',
           '}',
           '.vs_instruction_part strong { ',
               'vertical-align: middle;',
           '}',
           '.vs_then { ',
               'top: 1.6em;',
               'left: 35%;',
               'text-align: center;',
               'width: 7%;',
           '}',
           '.vs_arrow_container {',
               'left: 0;',
               'position: static;',
               'text-align: center;',
               'width: 35%;',
           '}',
           '.vs_arrow {',
               'background: url(http://com.vidoop.demo-static.s3.amazonaws.com/images/arrow.gif) center bottom no-repeat scroll;',
               'height: 22px;',
               'margin: auto;',
               'position: relative;',
               'top: -1px;',
               'width: 33px;',
           '}',
           '.vs_arrow_container .vs_instruction_part {',
               'left: 0;',
               'position: static;',
               'width: auto;',
           '}',
            '.vs_instruction_part { vertical-align: middle; }',
            '.vs_current_category { padding: 3px 1px 3px; }',
            '.vs_arrow_container .vs_current_category { font-weight: bold; }',
            '.vs_done { text-decoration: line-through; }',
            '.vs_instruction_part strong { color: #b2b2b2; font-size: 25px; font-weight: normal; padding-right: 4px; }',
           '</style>'
        ].join("\n")).prependTo("head");
    }

    // Create instructions for solving the image shield.
    function get_instructions() {
        return vidoop_secure.instructions;
    }

    add_style();

    // vidoop_secure node contains several important things:
    // * a hidden input field with the captcha ID as its value
    // * a text box that determines when captcha is shown
    // * an image that the user can click to see the captcha
    // * a comment node with the categories to choose
    var $input = $('.vidoop_secure input:text');
    var captcha = new Captcha($input, $('.vidoop_secure input:hidden').val());
    $('.vidoop_secure .vs_button').click(function(e) {
        captcha.slide_toggle();
    });
});
